package org.luxor.cloud.gateway.component;

import lombok.SneakyThrows;
import org.apache.commons.lang.StringUtils;
import org.springframework.boot.autoconfigure.web.ErrorProperties;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.reactive.error.DefaultErrorWebExceptionHandler;
import org.springframework.boot.web.reactive.error.ErrorAttributes;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

import java.util.HashMap;
import java.util.Map;

import static org.springframework.web.reactive.function.server.RequestPredicates.all;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;

/**
 * 全局统一的异常
 *
 * @author Mr.Yan  @date: 2020/11/12
 */
public class GlobalExceptionHandler extends DefaultErrorWebExceptionHandler {

    /**
     * Create a new {@see DefaultErrorWebExceptionHandler} instance.
     *
     * @param errorAttributes    the error attributes
     * @param resourceProperties the resources configuration properties
     * @param errorProperties    the error configuration properties
     * @param applicationContext the current application context
     */
    public GlobalExceptionHandler(ErrorAttributes errorAttributes, ResourceProperties resourceProperties,
                                  ErrorProperties errorProperties, ApplicationContext applicationContext) {
        super(errorAttributes, resourceProperties, errorProperties, applicationContext);
    }

    @Override
    public RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
        // 选择哪种响应视图？
        return route(acceptsTextHtml(), this::renderErrorView).andRoute(all(), this::renderErrorResponse);
    }

    @SneakyThrows
    @Override
    protected Mono<ServerResponse> renderDefaultErrorView(ServerResponse.BodyBuilder responseBody, Map<String, Object> error) {
        Map<String, Object> globalError = new HashMap<>(5);
        globalError.put("code", error.get("status"));
        globalError.put("message", error.get("message") == null ? error.get("error") : error.get("message"));
        globalError.put("data", StringUtils.EMPTY);

        StringBuilder builder = new StringBuilder();
        builder.append("<html><head><title>Error</title></head><body>");
        builder.append(Jackson2Mapper.toJsonPretty(globalError));
        builder.append("</body></html>");

        return responseBody.syncBody(builder.toString());
    }


    @Override
    public Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
        Map<String, Object> error = getErrorAttributes(request, this.getErrorAttributeOptions(request, MediaType.ALL));

        Map<String, Object> globalError = new HashMap<>(5);
        globalError.put("code", error.get("status"));
        globalError.put("message", error.get("message") == null ? error.get("error") : error.get("message"));
        globalError.put("data", StringUtils.EMPTY);

        return ServerResponse.status(HttpStatus.OK)
                .contentType(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromValue(globalError));
    }
}
